package cn.nkpro.elcube.task.impl;

import cn.nkpro.elcube.task.NkBpmDefService;
import cn.nkpro.elcube.task.NkDmnService;
import cn.nkpro.elcube.task.model.DmnDebugResult;
import cn.nkpro.elcube.task.model.DmnResult;
import cn.nkpro.elcube.task.model.ResourceDefinition;
import org.camunda.bpm.dmn.engine.*;
import org.camunda.bpm.dmn.engine.delegate.DmnDecisionTableEvaluationEvent;
import org.camunda.bpm.dmn.engine.delegate.DmnEvaluatedDecisionRule;
import org.camunda.bpm.dmn.engine.impl.DmnDecisionLiteralExpressionImpl;
import org.camunda.bpm.dmn.engine.impl.DmnDecisionTableImpl;
import org.camunda.bpm.model.dmn.HitPolicy;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import java.io.ByteArrayInputStream;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

@Service
public class NkDmnServiceImpl implements NkDmnService {

    @SuppressWarnings("all")
    @Autowired
    private NkBpmDefService defService;

    @Override
    public DmnResult exec(String key, Map<String,Object> inputs){
        ResourceDefinition dmnDefinition = defService.getDmnDefinitionByKey(key);
        Assert.notNull(dmnDefinition,"DMN配置没有找到");

        DmnDecision decision = dmnEngine.parseDecision(key,new ByteArrayInputStream(dmnDefinition.getXml().getBytes()));

        try{
            DmnDecisionResult dmnDecisionResult = dmnEngine.evaluateDecision(decision, inputs);

            DmnResult result = new DmnResult();
            if(threadLocal.get()!=null){
                result.setMatchedRules(
                        threadLocal.get().getMatchingRules().stream()
                        .map(DmnEvaluatedDecisionRule::getId)
                        .collect(Collectors.toList())
                );

                result.setFields(((DmnDecisionTableImpl) decision.getDecisionLogic()).getOutputs()
                        .stream()
                        .map(output-> {
                            Map<String,String> map = new HashMap<>();
                            map.put("title",output.getName());
                            map.put("field",output.getOutputName());
                            return map;
                        }).collect(Collectors.toList()));

                HitPolicy hitPolicy = ((DmnDecisionTableImpl) decision.getDecisionLogic()).getHitPolicyHandler()
                        .getHitPolicyEntry().getHitPolicy();
                if(hitPolicy==HitPolicy.COLLECT||hitPolicy==HitPolicy.RULE_ORDER||hitPolicy==HitPolicy.OUTPUT_ORDER){
                    result.setResult(new HashMap<>());
                    ((DmnDecisionTableImpl) decision.getDecisionLogic()).getOutputs()
                            .forEach(output->
                                    result.getResult().put(
                                            output.getOutputName(),
                                            dmnDecisionResult.collectEntries(output.getOutputName())
                                    )
                            );
                }else{
                    result.setResult(dmnDecisionResult.getSingleEntry());
                }
            }else{
                DmnDecisionLiteralExpressionImpl decisionLogic = (DmnDecisionLiteralExpressionImpl) decision.getDecisionLogic();

                Map<String,String> map = new HashMap<>();
                map.put("title",decision.getName());
                map.put("field",decisionLogic.getVariable().getName());

                result.setFields(Collections.singletonList(map));
                result.setResult(dmnDecisionResult.getSingleResult().getEntryMap());
            }
            return result;
        }finally {
            threadLocal.remove();
        }
    }

    @Override
    public Map<String,Object> debug(String key, Map<String,Object> inputs){
        ResourceDefinition dmnDefinition = defService.getDmnDefinitionByKey(key);
        Assert.notNull(dmnDefinition,"DMN配置没有找到");
        DmnDecision decision = dmnEngine.parseDecision(key,new ByteArrayInputStream(dmnDefinition.getXml().getBytes()));
        return evaluateDecision(decision, inputs, new HashMap<>());
    }

    private Map<String,Object> evaluateDecision(DmnDecision decision, Map<String,Object> variables, Map<String,Object> result){
        try{
            result.put(decision.getKey(),new DmnDebugResult(
                    dmnEngine.evaluateDecision(decision, variables),
                    threadLocal.get()!=null?
                            threadLocal.get()
                                    .getMatchingRules()
                                    .stream()
                                    .map(DmnEvaluatedDecisionRule::getId)
                                    .collect(Collectors.toList())
                            :null
            ));
            decision.getRequiredDecisions().forEach(dmnDecision -> evaluateDecision(dmnDecision,variables,result));
            return result;
        }finally {
            threadLocal.remove();
        }
    }

    private ThreadLocal<DmnDecisionTableEvaluationEvent> threadLocal = new ThreadLocal<>();
    private DmnEngine dmnEngine = DmnEngineConfiguration.createDefaultDmnEngineConfiguration()
            .customPostDecisionTableEvaluationListeners(Collections.singletonList(dmnDecisionTableEvaluationEvent -> threadLocal.set(dmnDecisionTableEvaluationEvent)))
            .buildEngine();

}
